/// A project representation.
///
/// A project manifest needs to be defined in a `Project.swift` manifest file.
/// Manifests need to import the framework ProjectDescription which contains all
/// the classes and enums that are available for you to describe your projects.
///
/// The snippet below shows an example project manifest:
///
/// ```swift
/// import ProjectDescription
///
/// let project = Project(
///     name: "MyProject",
///     organizationName: "MyOrg",
///     targets: [
///         .target(
///             name: "App",
///             destinations: .iOS,
///             product: .app,
///             bundleId: "dev.tuist.App",
///             infoPlist: "Config/App-Info.plist",
///             sources: ["Sources/**"],
///             resources: [
///                 "Resources/**",
///                 .folderReference(path: "Stubs"),
///                 .folderReference(path: "ODR", tags: ["odr_tag"])
///             ],
///             headers: .headers(
///                 public: ["Sources/public/A/**", "Sources/public/B/**"],
///                 private: "Sources/private/**",
///                 project: ["Sources/project/A/**", "Sources/project/B/**"]
///             ),
///             dependencies: [
///                 .project(target: "Framework1", path: "../Framework1"),
///                 .project(target: "Framework2", path: "../Framework2")
///             ]
///         )
///     ],
///     schemes: [
///         Scheme(
///             name: "App-Debug",
///             shared: true,
///             buildAction: .buildAction(targets: ["App"]),
///             testAction: .targets(["AppTests"]),
///             runAction: .runAction(executable: "App")
///         ),
///         Scheme(
///             name: "App-Release",
///             shared: true,
///             buildAction: .buildAction(targets: ["App"]),
///             runAction: .runAction(executable: "App")
///         )
///     ],
///     additionalFiles: [
///         "Dangerfile.swift",
///         "Documentation/**",
///         .folderReference(path: "Website")
///     ]
/// )
/// ```
public struct Project: Codable, Equatable, Sendable {
    /// The name of the project. Also, the file name of the generated Xcode project.
    public let name: String
    /// The name of the organization used by Xcode as copyright.
    public let organizationName: String?
    /// The prefix for class files Xcode generates when you create a project or class file.
    public let classPrefix: String?
    /// The project options.
    public let options: Options
    /// The Swift Packages used by the project.
    public let packages: [Package]
    /// The targets of the project.
    public let targets: [Target]
    /// The custom schemes for the project. Default schemes for each target are generated by default.
    public let schemes: [Scheme]
    /// The build settings and configuration for the project.
    public let settings: Settings?
    /// The custom file header template for Xcode built-in file templates.
    public let fileHeaderTemplate: FileHeaderTemplate?
    /// The additional files for the project. For target's additional files, see ``Target/additionalFiles``.
    public let additionalFiles: [FileElement]
    /// The resource synthesizers for the project to generate accessors for resources.
    public let resourceSynthesizers: [ResourceSynthesizer]

    public init(
        name: String,
        organizationName: String? = nil,
        classPrefix: String? = nil,
        options: Options = .options(),
        packages: [Package] = [],
        settings: Settings? = nil,
        targets: [Target] = [],
        schemes: [Scheme] = [],
        fileHeaderTemplate: FileHeaderTemplate? = nil,
        additionalFiles: [FileElement] = [],
        resourceSynthesizers: [ResourceSynthesizer] = .default
    ) {
        self.name = name
        self.organizationName = organizationName
        self.classPrefix = classPrefix
        self.options = options
        self.packages = packages
        self.targets = targets
        self.schemes = schemes
        self.settings = settings
        self.additionalFiles = additionalFiles
        self.fileHeaderTemplate = fileHeaderTemplate
        self.resourceSynthesizers = resourceSynthesizers
        dumpIfNeeded(self)
    }

    /// The project contains targets that depend on external dependencies
    public var containsExternalDependencies: Bool {
        targets.contains(where: { target in
            target.dependencies.contains(where: {
                if case .external = $0 {
                    return true
                } else {
                    return false
                }
            })
        })
    }
}
